<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>1.元数据和装饰器 | 被删的前端游乐场</title>
    <meta name="generator" content="VuePress 1.8.2">
    
    <meta name="description" content="Just playing around">
    
    <link rel="preload" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css" as="style"><link rel="preload" href="/front-end-playground/assets/js/app.1e2670bf.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/2.38d016d1.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/3.e3f029cb.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/110.5fbcb5e2.js" as="script">
    <link rel="stylesheet" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/front-end-playground/" class="home-link router-link-active"><!----> <span class="site-name">被删的前端游乐场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link router-link-active">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link router-link-active">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-2b653b36><span class="stand" data-v-2b653b36></span> <div class="cat" data-v-2b653b36><div class="body" data-v-2b653b36></div> <div class="head" data-v-2b653b36><div class="ear" data-v-2b653b36></div> <div class="ear" data-v-2b653b36></div></div> <div class="face" data-v-2b653b36><div class="nose" data-v-2b653b36></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div></div> <div class="tail-container" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>Angular框架解读</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-0-prestart.html" class="sidebar-link">0.预热篇</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-metadata.html" aria-current="page" class="active sidebar-link">1.元数据和装饰器</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-metadata.html#装饰器与元数据" class="sidebar-link">装饰器与元数据</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-metadata.html#angular-中的装饰器和元数据" class="sidebar-link">Angular 中的装饰器和元数据</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-metadata.html#总结" class="sidebar-link">总结</a></li></ul></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-dom-define.html" class="sidebar-link">2.视图抽象定义</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-zonejs.html" class="sidebar-link">3.Zone区域之zone.js</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-zone-ngzone.html" class="sidebar-link">4.Zone区域之ngZone</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-module.html" class="sidebar-link">5.模块化组织</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-di-1-basic-concepts.html" class="sidebar-link">6.依赖注入的基本概念</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-di-2-hierarchical-di.html" class="sidebar-link">7.多级依赖注入设计</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-di-3-bootstrap.html" class="sidebar-link">8.依赖注入的引导过程</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-ivy-0-design.html" class="sidebar-link">9.Ivy编译器整体设计</a></li><li><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-ivy-1-view-data-and-node-injector.html" class="sidebar-link">10.Ivy编译器的视图数据和依赖解析</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>玩转 Angular</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>试试 Cycle.js</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>D3.js 和 Tree</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>试试 Three.js</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>BOX2D.js 文档与教程</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><p>作为“为大型前端项目”而设计的前端框架，Angular 其实有许多值得参考和学习的设计，本系列主要用于研究这些设计和功能的实现原理。本文主要围绕 Angular 中随处可见的元数据，来进行介绍。</p> <p>装饰器是使用 Angular 进行开发时的核心概念。在 Angular 中，装饰器用于为类或属性附加元数据，来让自己知道那些类或属性的含义，以及该如何处理它们。</p> <h2 id="装饰器与元数据"><a href="#装饰器与元数据" class="header-anchor">#</a> 装饰器与元数据</h2> <p>不管是装饰器还是元数据，都不是由 Angular 提出的概念。因此，我们先来简单了解一下。</p> <h3 id="元数据-metadata"><a href="#元数据-metadata" class="header-anchor">#</a> 元数据（Metadata）</h3> <p>在通用的概念中，元数据是描述用户数据的数据。它总结了有关数据的基本信息，可以使查找和使用特定数据实例更加容易。例如，作者，创建日期，修改日期和文件大小是非常基本的文档元数据的示例。</p> <p>在用于类的场景下，元数据用于装饰类，来描述类的定义和行为，以便可以配置类的预期行为。</p> <h3 id="装饰器-decorator"><a href="#装饰器-decorator" class="header-anchor">#</a> 装饰器（Decorator）</h3> <p>装饰器是 JavaScript 的一种语言特性，是一项位于阶段 2（stage 2）的试验特性。</p> <p>装饰器是定义期间在类，类元素或其他 JavaScript 语法形式上调用的函数。</p> <p>装饰器具有三个主要功能：</p> <ol><li>可以用具有相同语义的匹配值替换正在修饰的值。（例如，装饰器可以将方法替换为另一种方法，将一个字段替换为另一个字段，将一个类替换为另一个类，等等）。</li> <li>可以将元数据与正在修饰的值相关联；可以从外部读取此元数据，并将其用于元编程和自我检查。</li> <li>可以通过元数据提供对正在修饰的值的访问。对于公共值，他们可以通过值名称来实现；对于私有值，它们接收访问器函数，然后可以选择共享它们。</li></ol> <p>本质上，装饰器可用于对值进行元编程和向其添加功能，而无需从根本上改变其外部行为。</p> <p>更多的内容，可以参考 <a href="https://github.com/tc39/proposal-decorators" target="_blank" rel="noopener noreferrer">tc39/proposal-decorators<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 提案。</p> <h2 id="angular-中的装饰器和元数据"><a href="#angular-中的装饰器和元数据" class="header-anchor">#</a> Angular 中的装饰器和元数据</h2> <p>我们在开发 Angular 应用时，不管是组件、指令，还是服务、模块等，都需要通过装饰器来进行定义和开发。装饰器会出现在类定义的紧前方，用来声明该类具有指定的类型，并且提供适合该类型的元数据。</p> <p>比如，我们可以用下列装饰器来声明 Angular 的类：<code>@Component()</code>、<code>@Directive()</code>、<code>@Pipe()</code>、<code>@Injectable()</code>、<code>@NgModule()</code>。</p> <h3 id="使用装饰器和元数据来改变类的行为"><a href="#使用装饰器和元数据来改变类的行为" class="header-anchor">#</a> 使用装饰器和元数据来改变类的行为</h3> <p>以<code>@Component()</code>为例，该装饰器的作用包括：</p> <ol><li>将类标记为 Angular 组件。</li> <li>提供可配置的元数据，用来确定应在运行时如何处理、实例化和使用该组件。</li></ol> <p>关于<code>@Component()</code>该如何使用可以参考<a href=""></a>，这里不多介绍。我们来看看这个装饰器的定义：</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token comment">// 提供 Angular 组件的配置元数据接口定义</span>
<span class="token comment">// Angular 中，组件是指令的子集，始终与模板相关联</span>
<span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">Component</span> <span class="token keyword">extends</span> <span class="token class-name">Directive</span> <span class="token punctuation">{</span>
  <span class="token comment">// changeDetection 用于此组件的变更检测策略</span>
  <span class="token comment">// 实例化组件时，Angular 将创建一个更改检测器，该更改检测器负责传播组件的绑定。</span>
  changeDetection<span class="token operator">?</span><span class="token operator">:</span> ChangeDetectionStrategy<span class="token punctuation">;</span>
  <span class="token comment">// 定义对其视图 DOM 子对象可见的可注入对象的集合</span>
  viewProviders<span class="token operator">?</span><span class="token operator">:</span> Provider<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token comment">// 包含组件的模块的模块ID，该组件必须能够解析模板和样式的相对 URL</span>
  moduleId<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token operator">...</span>
  <span class="token comment">// 模板和 CSS 样式的封装策略</span>
  encapsulation<span class="token operator">?</span><span class="token operator">:</span> ViewEncapsulation<span class="token punctuation">;</span>
  <span class="token comment">// 覆盖默认的插值起始和终止定界符（`{{`和`}}`）</span>
  interpolation<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 组件装饰器和元数据</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> Component<span class="token operator">:</span> ComponentDecorator <span class="token operator">=</span> <span class="token function">makeDecorator</span><span class="token punctuation">(</span>
    <span class="token string">'Component'</span><span class="token punctuation">,</span>
    <span class="token comment">// 使用默认的 CheckAlways 策略，在该策略中，更改检测是自动进行的，直到明确停用为止。</span>
    <span class="token punctuation">(</span>c<span class="token operator">:</span> Component <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>changeDetection<span class="token operator">:</span> ChangeDetectionStrategy<span class="token punctuation">.</span>Default<span class="token punctuation">,</span> <span class="token operator">...</span>c<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    Directive<span class="token punctuation">,</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span>
    <span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token builtin">any</span><span class="token operator">&gt;</span><span class="token punctuation">,</span> meta<span class="token operator">:</span> Component<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">SWITCH_COMPILE_COMPONENT</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> meta<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>以上便是组件装饰、组件元数据的定义，我们来看看装饰器的创建过程。</p> <h3 id="装饰器的创建过程"><a href="#装饰器的创建过程" class="header-anchor">#</a> 装饰器的创建过程</h3> <p>我们可以从源码中找到，组件和指令的装饰器都会通过<code>makeDecorator()</code>来产生：</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">makeDecorator</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>
    name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> props<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">any</span><span class="token punctuation">,</span> parentClass<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span> <span class="token comment">// 装饰器名字和属性</span>
    additionalProcessing<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">,</span>
    typeFn<span class="token operator">?</span><span class="token operator">:</span> <span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">,</span> <span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">)</span><span class="token operator">:</span>
    <span class="token punctuation">{</span><span class="token keyword">new</span> <span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">;</span> <span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">;</span> <span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">(</span>cls<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">any</span><span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token punctuation">{</span>
  <span class="token comment">// noSideEffects 用于确认闭包编译器包装的函数没有副作用</span>
  <span class="token keyword">return</span> <span class="token function">noSideEffects</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> 
    <span class="token keyword">const</span> metaCtor <span class="token operator">=</span> <span class="token function">makeMetadataCtor</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 装饰器工厂</span>
    <span class="token keyword">function</span> <span class="token function">DecoratorFactory</span><span class="token punctuation">(</span>
        <span class="token keyword">this</span><span class="token operator">:</span> <span class="token builtin">unknown</span><span class="token operator">|</span><span class="token keyword">typeof</span> DecoratorFactory<span class="token punctuation">,</span> <span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token punctuation">(</span>cls<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">any</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token keyword">instanceof</span> <span class="token class-name">DecoratorFactory</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 赋值元数据</span>
        <span class="token function">metaCtor</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token keyword">as</span> <span class="token keyword">typeof</span> DecoratorFactory<span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 创建装饰器工厂</span>
      <span class="token keyword">const</span> annotationInstance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token punctuation">(</span>DecoratorFactory <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token function">TypeDecorator</span><span class="token punctuation">(</span>cls<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 编译类</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>typeFn<span class="token punctuation">)</span> <span class="token function">typeFn</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> <span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 使用 Object.defineProperty 很重要，因为它会创建不可枚举的属性，从而防止该属性在子类化过程中被复制。</span>
        <span class="token keyword">const</span> annotations <span class="token operator">=</span> cls<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token constant">ANNOTATIONS</span><span class="token punctuation">)</span> <span class="token operator">?</span>
            <span class="token punctuation">(</span>cls <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token constant">ANNOTATIONS</span><span class="token punctuation">]</span> <span class="token operator">:</span>
            Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>cls<span class="token punctuation">,</span> <span class="token constant">ANNOTATIONS</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>value<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token constant">ANNOTATIONS</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        annotations<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>annotationInstance<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 特定逻辑的执行</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>additionalProcessing<span class="token punctuation">)</span> <span class="token function">additionalProcessing</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> cls<span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>parentClass<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 继承父类</span>
      DecoratorFactory<span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>parentClass<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    DecoratorFactory<span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>ngMetadataName <span class="token operator">=</span> name<span class="token punctuation">;</span>
    <span class="token punctuation">(</span>DecoratorFactory <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token punctuation">.</span>annotationCls <span class="token operator">=</span> DecoratorFactory<span class="token punctuation">;</span>
    <span class="token keyword">return</span> DecoratorFactory <span class="token keyword">as</span> <span class="token builtin">any</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>在上面的例子中，我们通过<code>makeDecorator()</code>产生了一个用于定义组件的<code>Component</code>装饰器工厂。当使用<code>@Component()</code>创建组件时，Angular 会根据元数据来编译组件。</p> <h3 id="根据装饰器元数据编译组件"><a href="#根据装饰器元数据编译组件" class="header-anchor">#</a> 根据装饰器元数据编译组件</h3> <p>Angular 会根据该装饰器元数据，来编译 Angular 组件，然后将生成的组件定义（<code>ɵcmp</code>）修补到组件类型上：</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">compileComponent</span><span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token builtin">any</span><span class="token operator">&gt;</span><span class="token punctuation">,</span> metadata<span class="token operator">:</span> Component<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
  <span class="token comment">// 初始化 ngDevMode</span>
  <span class="token punctuation">(</span><span class="token keyword">typeof</span> ngDevMode <span class="token operator">===</span> <span class="token string">'undefined'</span> <span class="token operator">||</span> ngDevMode<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">initNgDevMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> ngComponentDef<span class="token operator">:</span> <span class="token builtin">any</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token comment">// 元数据可能具有需要解析的资源</span>
  <span class="token function">maybeQueueResolutionOfComponentResources</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> metadata<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 这里使用的功能与指令相同，因为这只是创建 ngFactoryDef 所需的元数据的子集</span>
  <span class="token function">addDirectiveFactoryDef</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> metadata<span class="token punctuation">)</span><span class="token punctuation">;</span>
  Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> <span class="token constant">NG_COMP_DEF</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    <span class="token function-variable function">get</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>ngComponentDef <span class="token operator">===</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> compiler <span class="token operator">=</span> <span class="token function">getCompilerFacade</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 根据元数据解析组件</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">componentNeedsResolution</span><span class="token punctuation">(</span>metadata<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token operator">...</span>
          <span class="token comment">// 异常处理</span>
        <span class="token punctuation">}</span>
        <span class="token operator">...</span>
        <span class="token comment">// 创建编译组件需要的完整元数据</span>
        <span class="token keyword">const</span> templateUrl <span class="token operator">=</span> metadata<span class="token punctuation">.</span>templateUrl <span class="token operator">||</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">ng:///</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>type<span class="token punctuation">.</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/template.html</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
        <span class="token keyword">const</span> meta<span class="token operator">:</span> R3ComponentMetadataFacade <span class="token operator">=</span> <span class="token punctuation">{</span>
          <span class="token operator">...</span><span class="token function">directiveMetadata</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> metadata<span class="token punctuation">)</span><span class="token punctuation">,</span>
          typeSourceSpan<span class="token operator">:</span> compiler<span class="token punctuation">.</span><span class="token function">createParseSourceSpan</span><span class="token punctuation">(</span><span class="token string">'Component'</span><span class="token punctuation">,</span> type<span class="token punctuation">.</span>name<span class="token punctuation">,</span> templateUrl<span class="token punctuation">)</span><span class="token punctuation">,</span>
          template<span class="token operator">:</span> metadata<span class="token punctuation">.</span>template <span class="token operator">||</span> <span class="token string">''</span><span class="token punctuation">,</span>
          preserveWhitespaces<span class="token punctuation">,</span>
          styles<span class="token operator">:</span> metadata<span class="token punctuation">.</span>styles <span class="token operator">||</span> <span class="token constant">EMPTY_ARRAY</span><span class="token punctuation">,</span>
          animations<span class="token operator">:</span> metadata<span class="token punctuation">.</span>animations<span class="token punctuation">,</span>
          directives<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
          changeDetection<span class="token operator">:</span> metadata<span class="token punctuation">.</span>changeDetection<span class="token punctuation">,</span>
          pipes<span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">Map</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
          encapsulation<span class="token punctuation">,</span>
          interpolation<span class="token operator">:</span> metadata<span class="token punctuation">.</span>interpolation<span class="token punctuation">,</span>
          viewProviders<span class="token operator">:</span> metadata<span class="token punctuation">.</span>viewProviders <span class="token operator">||</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>
        <span class="token comment">// 编译过程需要计算深度，以便确认编译是否最终完成</span>
        compilationDepth<span class="token operator">++</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>meta<span class="token punctuation">.</span>usesInheritance<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token function">addDirectiveDefToUndecoratedParents</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token punctuation">}</span>
          <span class="token comment">// 根据模板、环境和组件需要的元数据，来编译组件</span>
          ngComponentDef <span class="token operator">=</span> compiler<span class="token punctuation">.</span><span class="token function">compileComponent</span><span class="token punctuation">(</span>angularCoreEnv<span class="token punctuation">,</span> templateUrl<span class="token punctuation">,</span> meta<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
          <span class="token comment">// 即使编译失败，也请确保减少编译深度</span>
          compilationDepth<span class="token operator">--</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>compilationDepth <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token comment">// 当执行 NgModule 装饰器时，我们将模块定义加入队列，以便仅在所有声明都已解析的情况下才将队列出队，并将其自身作为模块作用域添加到其所有声明中</span>
          <span class="token comment">// 此调用运行检查以查看队列中的任何模块是否可以出队，并将范围添加到它们的声明中</span>
          <span class="token function">flushModuleScopingQueueAsMuchAsPossible</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// 如果组件编译是异步的，则声明该组件的 @NgModule 批注可以执行并在组件类型上设置 ngSelectorScope 属性</span>
        <span class="token comment">// 这允许组件在完成编译后，使用模块中的 directiveDefs 对其自身进行修补</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">hasSelectorScope</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">const</span> scopes <span class="token operator">=</span> <span class="token function">transitiveScopesFor</span><span class="token punctuation">(</span>type<span class="token punctuation">.</span>ngSelectorScope<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token function">patchComponentDefWithScope</span><span class="token punctuation">(</span>ngComponentDef<span class="token punctuation">,</span> scopes<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
      <span class="token keyword">return</span> ngComponentDef<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token operator">...</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>编译组件的过程可能是异步的（比如需要解析组件模板或其他资源的 URL）。如果编译不是立即进行的，<code>compileComponent</code>会将资源解析加入到全局队列中，并且将无法返回<code>ɵcmp</code>，直到通过调用<code>resolveComponentResources</code>解决了全局队列为止。</p> <h3 id="编译过程中的元数据"><a href="#编译过程中的元数据" class="header-anchor">#</a> 编译过程中的元数据</h3> <p>元数据是有关类的信息，但它不是类的属性。因此，用于配置类的定义和行为的这些数据，不应该存储在该类的实例中，我们还需要在其他地方保存此数据。</p> <p>在 Angular 中，编译过程产生的元数据，会使用<code>CompileMetadataResolver</code>来进行管理和维护，这里我们主要看指令（组件）相关的逻辑：</p> <div class="language-ts extra-class"><pre class="language-ts"><code><span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">CompileMetadataResolver</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> _nonNormalizedDirectiveCache <span class="token operator">=</span>
      <span class="token keyword">new</span> <span class="token class-name">Map<span class="token operator">&lt;</span>Type<span class="token punctuation">,</span> <span class="token punctuation">{</span>annotation<span class="token operator">:</span> Directive<span class="token punctuation">,</span> metadata<span class="token operator">:</span> cpl<span class="token punctuation">.</span>CompileDirectiveMetadata<span class="token punctuation">}</span><span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 使用 Map 的方式来保存</span>
  <span class="token keyword">private</span> _directiveCache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map<span class="token operator">&lt;</span>Type<span class="token punctuation">,</span> cpl<span class="token punctuation">.</span>CompileDirectiveMetadata<span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
  <span class="token keyword">private</span> _summaryCache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map<span class="token operator">&lt;</span>Type<span class="token punctuation">,</span> cpl<span class="token punctuation">.</span>CompileTypeSummary<span class="token operator">|</span><span class="token keyword">null</span><span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> _pipeCache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map<span class="token operator">&lt;</span>Type<span class="token punctuation">,</span> cpl<span class="token punctuation">.</span>CompilePipeMetadata<span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> _ngModuleCache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map<span class="token operator">&lt;</span>Type<span class="token punctuation">,</span> cpl<span class="token punctuation">.</span>CompileNgModuleMetadata<span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> _ngModuleOfTypes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map<span class="token operator">&lt;</span>Type<span class="token punctuation">,</span> Type<span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> _shallowModuleCache <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map<span class="token operator">&lt;</span>Type<span class="token punctuation">,</span> cpl<span class="token punctuation">.</span>CompileShallowModuleMetadata<span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token function">constructor</span><span class="token punctuation">(</span>
      <span class="token keyword">private</span> _config<span class="token operator">:</span> CompilerConfig<span class="token punctuation">,</span> <span class="token keyword">private</span> _htmlParser<span class="token operator">:</span> HtmlParser<span class="token punctuation">,</span>
      <span class="token keyword">private</span> _ngModuleResolver<span class="token operator">:</span> NgModuleResolver<span class="token punctuation">,</span> <span class="token keyword">private</span> _directiveResolver<span class="token operator">:</span> DirectiveResolver<span class="token punctuation">,</span>
      <span class="token keyword">private</span> _pipeResolver<span class="token operator">:</span> PipeResolver<span class="token punctuation">,</span> <span class="token keyword">private</span> _summaryResolver<span class="token operator">:</span> SummaryResolver<span class="token operator">&lt;</span><span class="token builtin">any</span><span class="token operator">&gt;</span><span class="token punctuation">,</span>
      <span class="token keyword">private</span> _schemaRegistry<span class="token operator">:</span> ElementSchemaRegistry<span class="token punctuation">,</span>
      <span class="token keyword">private</span> _directiveNormalizer<span class="token operator">:</span> DirectiveNormalizer<span class="token punctuation">,</span> <span class="token keyword">private</span> _console<span class="token operator">:</span> Console<span class="token punctuation">,</span>
      <span class="token keyword">private</span> _staticSymbolCache<span class="token operator">:</span> StaticSymbolCache<span class="token punctuation">,</span> <span class="token keyword">private</span> _reflector<span class="token operator">:</span> CompileReflector<span class="token punctuation">,</span>
      <span class="token keyword">private</span> _errorCollector<span class="token operator">?</span><span class="token operator">:</span> ErrorCollector<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token comment">// 清除特定某个指令的元数据</span>
  <span class="token function">clearCacheFor</span><span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> dirMeta <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_directiveCache<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_directiveCache<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 清除所有元数据</span>
  <span class="token function">clearCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_directiveCache<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>
  <span class="token punctuation">}</span>
  <span class="token comment">/**
   * 加载 NgModule 中，已声明的指令和的管道
   */</span>
  <span class="token function">loadNgModuleDirectiveAndPipeMetadata</span><span class="token punctuation">(</span>moduleType<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span> isSync<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">,</span> throwIfNotFound <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token operator">:</span>
      <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token builtin">any</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> ngModule <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getNgModuleMetadata</span><span class="token punctuation">(</span>moduleType<span class="token punctuation">,</span> throwIfNotFound<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> loading<span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token builtin">any</span><span class="token operator">&gt;</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>ngModule<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      ngModule<span class="token punctuation">.</span>declaredDirectives<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> promise <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">loadDirectiveMetadata</span><span class="token punctuation">(</span>moduleType<span class="token punctuation">,</span> id<span class="token punctuation">.</span>reference<span class="token punctuation">,</span> isSync<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>promise<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          loading<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>promise<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      ngModule<span class="token punctuation">.</span>declaredPipes<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_loadPipeMetadata</span><span class="token punctuation">(</span>id<span class="token punctuation">.</span>reference<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span>loading<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 加载指令（组件）元数据</span>
  <span class="token function">loadDirectiveMetadata</span><span class="token punctuation">(</span>ngModuleType<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span> directiveType<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span> isSync<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span><span class="token operator">:</span> SyncAsync<span class="token operator">&lt;</span><span class="token keyword">null</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// 若已加载，则直接返回</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_directiveCache<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    directiveType <span class="token operator">=</span> <span class="token function">resolveForwardRef</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span>annotation<span class="token punctuation">,</span> metadata<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getNonNormalizedDirectiveMetadata</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">)</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token comment">// 创建指令（组件）元数据</span>
    <span class="token keyword">const</span> <span class="token function-variable function">createDirectiveMetadata</span> <span class="token operator">=</span> <span class="token punctuation">(</span>templateMetadata<span class="token operator">:</span> cpl<span class="token punctuation">.</span>CompileTemplateMetadata<span class="token operator">|</span><span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">const</span> normalizedDirMeta <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">cpl</span><span class="token punctuation">.</span><span class="token function">CompileDirectiveMetadata</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        isHost<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        type<span class="token operator">:</span> metadata<span class="token punctuation">.</span>type<span class="token punctuation">,</span>
        isComponent<span class="token operator">:</span> metadata<span class="token punctuation">.</span>isComponent<span class="token punctuation">,</span>
        selector<span class="token operator">:</span> metadata<span class="token punctuation">.</span>selector<span class="token punctuation">,</span>
        exportAs<span class="token operator">:</span> metadata<span class="token punctuation">.</span>exportAs<span class="token punctuation">,</span>
        changeDetection<span class="token operator">:</span> metadata<span class="token punctuation">.</span>changeDetection<span class="token punctuation">,</span>
        inputs<span class="token operator">:</span> metadata<span class="token punctuation">.</span>inputs<span class="token punctuation">,</span>
        outputs<span class="token operator">:</span> metadata<span class="token punctuation">.</span>outputs<span class="token punctuation">,</span>
        hostListeners<span class="token operator">:</span> metadata<span class="token punctuation">.</span>hostListeners<span class="token punctuation">,</span>
        hostProperties<span class="token operator">:</span> metadata<span class="token punctuation">.</span>hostProperties<span class="token punctuation">,</span>
        hostAttributes<span class="token operator">:</span> metadata<span class="token punctuation">.</span>hostAttributes<span class="token punctuation">,</span>
        providers<span class="token operator">:</span> metadata<span class="token punctuation">.</span>providers<span class="token punctuation">,</span>
        viewProviders<span class="token operator">:</span> metadata<span class="token punctuation">.</span>viewProviders<span class="token punctuation">,</span>
        queries<span class="token operator">:</span> metadata<span class="token punctuation">.</span>queries<span class="token punctuation">,</span>
        guards<span class="token operator">:</span> metadata<span class="token punctuation">.</span>guards<span class="token punctuation">,</span>
        viewQueries<span class="token operator">:</span> metadata<span class="token punctuation">.</span>viewQueries<span class="token punctuation">,</span>
        entryComponents<span class="token operator">:</span> metadata<span class="token punctuation">.</span>entryComponents<span class="token punctuation">,</span>
        componentViewType<span class="token operator">:</span> metadata<span class="token punctuation">.</span>componentViewType<span class="token punctuation">,</span>
        rendererType<span class="token operator">:</span> metadata<span class="token punctuation">.</span>rendererType<span class="token punctuation">,</span>
        componentFactory<span class="token operator">:</span> metadata<span class="token punctuation">.</span>componentFactory<span class="token punctuation">,</span>
        template<span class="token operator">:</span> templateMetadata
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>templateMetadata<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">initComponentFactory</span><span class="token punctuation">(</span>metadata<span class="token punctuation">.</span>componentFactory<span class="token operator">!</span><span class="token punctuation">,</span> templateMetadata<span class="token punctuation">.</span>ngContentSelectors<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 存储完整的元数据信息，以及元数据摘要信息</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>_directiveCache<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">,</span> normalizedDirMeta<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>_summaryCache<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">,</span> normalizedDirMeta<span class="token punctuation">.</span><span class="token function">toSummary</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>metadata<span class="token punctuation">.</span>isComponent<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// 如果是组件，该过程可能为异步过程，则需要等待异步过程结束后的模板返回</span>
      <span class="token keyword">const</span> template <span class="token operator">=</span> metadata<span class="token punctuation">.</span>template <span class="token operator">!</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> templateMeta <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_directiveNormalizer<span class="token punctuation">.</span><span class="token function">normalizeTemplate</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        ngModuleType<span class="token punctuation">,</span>
        componentType<span class="token operator">:</span> directiveType<span class="token punctuation">,</span>
        moduleUrl<span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_reflector<span class="token punctuation">.</span><span class="token function">componentModuleUrl</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">,</span> annotation<span class="token punctuation">)</span><span class="token punctuation">,</span>
        encapsulation<span class="token operator">:</span> template<span class="token punctuation">.</span>encapsulation<span class="token punctuation">,</span>
        template<span class="token operator">:</span> template<span class="token punctuation">.</span>template<span class="token punctuation">,</span>
        templateUrl<span class="token operator">:</span> template<span class="token punctuation">.</span>templateUrl<span class="token punctuation">,</span>
        styles<span class="token operator">:</span> template<span class="token punctuation">.</span>styles<span class="token punctuation">,</span>
        styleUrls<span class="token operator">:</span> template<span class="token punctuation">.</span>styleUrls<span class="token punctuation">,</span>
        animations<span class="token operator">:</span> template<span class="token punctuation">.</span>animations<span class="token punctuation">,</span>
        interpolation<span class="token operator">:</span> template<span class="token punctuation">.</span>interpolation<span class="token punctuation">,</span>
        preserveWhitespaces<span class="token operator">:</span> template<span class="token punctuation">.</span>preserveWhitespaces
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isPromise</span><span class="token punctuation">(</span>templateMeta<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> isSync<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_reportError</span><span class="token punctuation">(</span><span class="token function">componentStillLoadingError</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">)</span><span class="token punctuation">,</span> directiveType<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      <span class="token comment">// 并将元数据进行存储</span>
      <span class="token keyword">return</span> SyncAsync<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>templateMeta<span class="token punctuation">,</span> createDirectiveMetadata<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token comment">// 指令，直接存储元数据</span>
      <span class="token function">createDirectiveMetadata</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 获取给定指令（组件）的元数据信息</span>
  <span class="token function">getDirectiveMetadata</span><span class="token punctuation">(</span>directiveType<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> cpl<span class="token punctuation">.</span>CompileDirectiveMetadata <span class="token punctuation">{</span>
    <span class="token keyword">const</span> dirMeta <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_directiveCache<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>directiveType<span class="token punctuation">)</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>
    <span class="token keyword">return</span> dirMeta<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token comment">// 获取给定指令（组件）的元数据摘要信息</span>
  <span class="token function">getDirectiveSummary</span><span class="token punctuation">(</span>dirType<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> cpl<span class="token punctuation">.</span>CompileDirectiveSummary <span class="token punctuation">{</span>
    <span class="token keyword">const</span> dirSummary <span class="token operator">=</span>
        <span class="token operator">&lt;</span>cpl<span class="token punctuation">.</span>CompileDirectiveSummary<span class="token operator">&gt;</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_loadSummary</span><span class="token punctuation">(</span>dirType<span class="token punctuation">,</span> cpl<span class="token punctuation">.</span>CompileSummaryKind<span class="token punctuation">.</span>Directive<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">...</span>
    <span class="token keyword">return</span> dirSummary<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>可以看到，在编译过程中，不管是组件、指令、管道，还是模块，这些类在编译过程中的元数据，都使用<code>Map</code>来存储。</p> <h2 id="总结"><a href="#总结" class="header-anchor">#</a> 总结</h2> <p>本节我们介绍了 Angular 中的装饰器和元数据，其中元数据用于描述类的定义和行为。</p> <p>在 Angular 编译过程中，会使用<code>Map</code>的数据结构来维护和存储装饰器的元数据，并根据这些元数据信息来编译组件、指令、管道和模块等。</p> <h3 id="参考"><a href="#参考" class="header-anchor">#</a> 参考</h3> <ul><li><a href="http://st.inf.tu-dresden.de/files/teaching/ss14/cbse/slides/11-cbse-metamodelling.pdf" target="_blank" rel="noopener noreferrer">11. Metadata, Metamodelling, and Metaprogramming<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li> <li><a href="http://nicholasjohnson.com/blog/how-angular2-di-works-with-typescript/" target="_blank" rel="noopener noreferrer">How does the TypeScript Angular DI magic work?<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/front-end-playground/edit/sourcecode/docs/front-end-others/deep-into-angular/angular-design-metadata.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----> <blockquote>部分文章中使用了一些网站的截图，如果涉及侵权，请告诉我删一下谢谢~</blockquote> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本文版权归作者所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。</div> <div>出处：被删的前端游乐场</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-0-prestart.html" class="prev">
          0.预热篇
        </a></span> <span class="next"><a href="/front-end-playground/front-end-others/deep-into-angular/angular-design-dom-define.html">
          2.视图抽象定义
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-921" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><span class="el-popover__reference-wrapper"><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty0.svg);"></div></span></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/front-end-playground/assets/js/app.1e2670bf.js" defer></script><script src="/front-end-playground/assets/js/2.38d016d1.js" defer></script><script src="/front-end-playground/assets/js/3.e3f029cb.js" defer></script><script src="/front-end-playground/assets/js/110.5fbcb5e2.js" defer></script>
  </body>
</html>
